package com.spzx.product.service.impl;

import com.spzx.common.core.utils.StringUtils;
import com.spzx.product.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class TestServiceImpl implements TestService {

   @Autowired
   private StringRedisTemplate stringRedisTemplate;
    
   @Override
   public void testLock() {
//      // 查询Redis中的num值
//      String value = this.stringRedisTemplate.opsForValue().get("num");
//      // 没有该值return
//      if (StringUtils.isBlank(value)){
//         return ;
//      }
//      // 有值就转成int
//      int num = Integer.parseInt(value);
//      // 把Redis中的num值+1
//      this.stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));
      // 使用Redis的原子性操作

      //0.先尝试获取锁 setnx key val
      //Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", "lock", 3, TimeUnit.SECONDS);
      //优化之UUID防误删
      String lock = "lock";
      String lockValue = UUID.randomUUID().toString();
      Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lock, lockValue, 3, TimeUnit.SECONDS);
      if(flag){
         //获取锁成功，执行业务代码
         //1.先从redis中通过key num获取值  key提前手动设置 num 初始值：0
         //版本二 加锁成功后设置过期时间
         //版本三 加锁和设置过期时间确保原子性
         //版本四 删除锁之前，判断当前线程是否加锁
         //版本五 解锁操作不具备原子性，使用lua脚本
//         stringRedisTemplate.expire("lock", 3, TimeUnit.SECONDS);
         try {
            String value = stringRedisTemplate.opsForValue().get("num");
            //2.如果值为空则非法直接返回即可
            if (StringUtils.isBlank(value)) {
               return;
            }
            //3.对num值进行自增加一
            int num = Integer.parseInt(value);
            stringRedisTemplate.opsForValue().set("num", String.valueOf(++num));
         } catch (NumberFormatException e) {
            throw new RuntimeException(e);
         }finally {
//            //判断当前线程是否加锁
//            String lockValue1 = stringRedisTemplate.opsForValue().get("lock");
//            if(lockValue.equals(lockValue1)){
//               //4.将锁释放
//               //stringRedisTemplate.delete("lock");
//               stringRedisTemplate.delete("lock");
//            }
            //使用lua脚本释放锁
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then\n" +
                    "    return redis.call('del', KEYS[1])\n" +
                    "else\n" +
                    "    return 0\n" +
                    "end";
            stringRedisTemplate.execute(
                    new DefaultRedisScript<>(script, Boolean.class),
                    Arrays.asList(lock),
                    lockValue);
         }
      }else{
         //获取锁失败，等待1秒后重试
         try {
            Thread.sleep(1000);
            System.out.println("重试");
            this.testLock();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}